using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections;
using DevExpress.CodeRush.Core;
using DevExpress.CodeRush.PlugInCore;
using DevExpress.CodeRush.StructuralParser;
using SP = DevExpress.CodeRush.StructuralParser;
using DX_Contrib;
using DX_Contrib.Controls;
using DX_Contrib.Painting;
using Blazing.DXCodeExtensions.Settings;
using System.Collections.Generic;
using EnvDTE;
/// <summary>
/// Summary description for PaintIt.
/// </summary>
public class PaintIt : StandardPlugIn
{
    private Painter Painter;
    private PaintItSettings Settings = new PaintItSettings();
    private LanguageElement SelectedMember;
    private LanguageElementCollection CurrentReferences = new LanguageElementCollection();

    // constructor...

    public PaintIt()
    {
        /// <summary>
        /// Required for Windows.Forms Class Composition Designer support
        /// </summary>
        InitializeComponent();
        LoadSettings();
    }

    public override void InitializePlugIn()
    {
        base.InitializePlugIn();

        //
        // TODO: Add your initialization code here.
        //
        Painter = new Painter();

        EditorPaint += PaintIt_EditorPaint;
        EditorMouseDown += PaintIt_EditorMouseDown;
        EditorCharacterTyping += PaintIt_EditorCharacterTyping;

        LanguageElementActivated += PaintIt_LanguageElementActivated;
        OptionsChanged += PaintIt_OptionsChanged;
        WindowActivated += PaintIt_WindowActivated;
    }

    DateTime _itemActivatedOnlyAfterTimeout = DateTime.MinValue;
    void PaintIt_EditorCharacterTyping(EditorCharacterTypingEventArgs ea)
    {
        _itemActivatedOnlyAfterTimeout = DateTime.Now.AddSeconds(5);
    }

    public override void FinalizePlugIn()
    {
        Painter.Dispose();
        base.FinalizePlugIn();
    }

    #region " Component Designer generated code "
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        ((System.ComponentModel.ISupportInitialize)this).BeginInit();
        //
        //PaintIt
        //
        ((System.ComponentModel.ISupportInitialize)this).EndInit();

    }
    #endregion

    #region "Utility"
    private bool IsScopePaintable(LanguageElement Element)
    {
        bool functionReturnValue = false;
        functionReturnValue = true;
        switch (Element.ElementType)
        {
            case LanguageElementType.InitializedVariable:
            case LanguageElementType.Variable:
            case LanguageElementType.Parameter:
            case LanguageElementType.Method:
            case LanguageElementType.Property:
                break;

            case LanguageElementType.ElementReferenceExpression:
            case LanguageElementType.ParameterReference:
            case LanguageElementType.MethodCall:
            case LanguageElementType.MethodCallExpression:
                break;

            default:
                functionReturnValue = false;
                break;
        }
        return functionReturnValue;
    }

    private bool IsDeclaration(LanguageElement Element)
    {
        return Element.ElementType == LanguageElementType.Variable ||
            Element.ElementType == LanguageElementType.Parameter ||
            Element.ElementType == LanguageElementType.InitializedVariable ||
            Element.ElementType == LanguageElementType.Property ||
            Element.ElementType == LanguageElementType.Method;
    }

    private bool IsHighlightCandidate(LanguageElement LE)
    {

        if (IsDeclaration(LE) || 
            LE.ElementType == LanguageElementType.ElementReferenceExpression ||
            LE.ElementType == LanguageElementType.MethodCallExpression || 
            LE.ElementType == LanguageElementType.MethodCall)
            return true;

        return false;
    }

    private LanguageElement GetDeclarationOrNothing(LanguageElement Element)
    {
        IElement DeclarationLite = Element.GetDeclaration();
        if (DeclarationLite == null)
        {
            return null;
        }
        LanguageElement Declaration = DeclarationLite.ToLanguageElement();
        if (Declaration == null)
        {
            return null;
        }
        return Declaration;
    }

    #endregion

    #region "Trigger Events "

    void PaintIt_WindowActivated(Window gotFocusWindow, Window lostFocusWindow)
    {
        InvalidateElements();
    }

    private void PaintIt_EditorPaint(DevExpress.CodeRush.Core.EditorPaintEventArgs ea)
    {
        if (Painter == null)
        {
            Painter = new Painter();
        }
        try
        {
            HighlightElements(ea);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            throw;
        }
    }

    private void PaintIt_EditorMouseDown(DevExpress.CodeRush.Core.EditorEventArgs ea)
    {
        try
        {
            if (!Settings.CurrentMemberSettings.Enabled)
            {
                SelectedMember = null;
                return;
            }
            if (Settings.CurrentMemberSettings.DetectionMethod == DetectionMethod.CurrentIsUnderCursor)
            {
                LanguageElement element = ea.TextView.GetLanguageElementUnderMouse();
                if (element != null)
                    RegenerateReferenceList(element, false);
                else
                    ClearElements();
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            throw;
        }
    }

    private void PaintIt_LanguageElementActivated(DevExpress.CodeRush.Core.LanguageElementActivatedEventArgs ea)
    {
        try
        {
            if (!Settings.CurrentMemberSettings.Enabled)
            {
                SelectedMember = null;
                return;
            }
            if (Settings.CurrentMemberSettings.DetectionMethod == DetectionMethod.CurrentIsUnderCaret &&
                ea != null && ea.Element != null)
            {
                if (DateTime.Now > _itemActivatedOnlyAfterTimeout)
                    RegenerateReferenceList(ea.Element, false);
                else
                    ClearElements();
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            throw;
        }
    }
    #endregion

    #region "Settings Options "
    private void PaintIt_OptionsChanged(DevExpress.CodeRush.Core.OptionsChangedEventArgs ea)
    {
        if (ea.OptionsPages.Contains(typeof(OptPaintIt)))
        {
            LoadSettings();
        }
    }
    private void LoadSettings()
    {
        DecoupledStorage Storage = OptPaintIt.Storage;
        {
            if (Storage.ReadBoolean(OptPaintIt.SECTION_CURRENT_MEMBER, "CurrentUnderCaret", true))
            {
                Settings.CurrentMemberSettings.DetectionMethod = DetectionMethod.CurrentIsUnderCaret;
            }
            else
            {
                Settings.CurrentMemberSettings.DetectionMethod = DetectionMethod.CurrentIsUnderCursor;
            }
            Settings.CurrentMemberSettings.Enabled = Storage.ReadBoolean(OptPaintIt.SECTION_CURRENT_MEMBER, "Enabled", true);
            Settings.CurrentMemberSettings.DisplayReferenceCount = Storage.ReadBoolean(OptPaintIt.SECTION_DISPLAY_REFERENCE_COUNT, "DisplayReferenceCount", true);
            Settings.CurrentMemberSettings.HighlightOtherReferences = Storage.ReadBoolean(OptPaintIt.SECTION_CURRENT_MEMBER, "HighlightOtherItems", true);
            DX_Contrib.Painting.Settings.LoadStyle(Storage, 
                OptPaintIt.SECTION_CURRENT_MEMBER,
                "", 
                Settings.CurrentMemberSettings.CurrentMemberExactOptions, 
                OptPaintIt.DefaultHighlightStyle());
            
            DX_Contrib.Painting.Settings.LoadStyle(Storage, 
                OptPaintIt.SECTION_CURRENT_MEMBER, 
                "", 
                Settings.CurrentMemberSettings.CurrentMemberSiblingOptions, 
                OptPaintIt.DefaultHighlightStyle());
            Settings.CurrentMemberSettings.CurrentMemberExactOptions.Enabled = Storage.ReadBoolean("CurrentMember", "HighlightCurrentItem", true);
        }

        Storage.Dispose();
    }

    #endregion

    #region "Current Member Painting"
    private void ClearElements()
    {
        if (CurrentReferences.Count == 0)
        {
            return;
        }
        InvalidateElements();
        CurrentReferences.Clear();
        SelectedMember = null;
    }

    private readonly object eventLock = new object();
    private void RegenerateReferenceList(LanguageElement StartElement, bool jumpNext)
    {
        try
        {
            if (StartElement == null)
                return;

            lock (eventLock)
            {
                ClearElements();
                if (!IsHighlightCandidate(StartElement))
                {
                    return;
                }
                SelectedMember = StartElement;
                IElement DeclarationLite;
                TextView ActiveView = CodeRush.TextViews.Active;
                if (IsDeclaration(StartElement))
                {
                    DeclarationLite = StartElement;
                }
                else
                {
                    if (!ParserServices.HasSourceTreeResolver)
                    {
                        ParserServices.RegisterSourceTreeResolver(new SourceTreeResolver());
                    }
                    DeclarationLite = ParserServices.SourceModelService.GetDeclaration(StartElement);
                }
                LanguageElement Declaration = DeclarationLite.ToLanguageElement();
                if ((Declaration != null))
                {
                    CurrentReferences.Add(Declaration);
                }
                CurrentReferences.AddRange(DeclarationLite.FindAllReferences(ActiveView.FileNode).ToLanguageElementCollection());
                int refIndex = -1;
                for (int i = 0; i < CurrentReferences.Count; i++)
                {
                    if (Elements.ElementsAreSame((LanguageElement)CurrentReferences[i], StartElement))
                    {
                        refIndex = i;
                        if (!jumpNext)
                            CurrentReferences.Remove((LanguageElement)CurrentReferences[i]);
                        break;
                    }

                }

                if (jumpNext && refIndex > -1 && CurrentReferences.Count > 1)
                {
                    int nextRef = refIndex + 1;
                    if (nextRef > CurrentReferences.Count - 1) nextRef = 0;

                    SelectedMember = CurrentReferences[nextRef];
                    CurrentReferences.RemoveAt(nextRef);

                    ActiveView.EnsureLineOffsetIsVisible(SelectedMember.NameRange.Start.Line, SelectedMember.NameRange.End.Offset);
                    ActiveView.Caret.MoveTo(SelectedMember.NameRange.Start.Line, SelectedMember.NameRange.Start.Offset + 1);
                }

                DevExpress.DXCore.Diagnostics.TextBuffers.Log.SendMsg("Found references: " + CurrentReferences.Count);

                InvalidateElements();
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            throw;
        }
    }
    private void InvalidateElements()
    {
        TextView ActiveView = CodeRush.TextViews.Active;
        InvalidateElement(SelectedMember, ActiveView);
        foreach (LanguageElement Element in CurrentReferences)
        {
            InvalidateElement(Element, ActiveView);
        }
        
    }
    private void InvalidateElement(LanguageElement Element, TextView ActiveView)
    {
        ActiveView.Invalidate(LargerRect(Element, ActiveView));
    }
    private void HighlightElements(EditorPaintEventArgs PaintArgs)
    {
        if (CurrentReferences.Count == 0)
        {
            return;
        }
        PaintOptions ExactOptions = Settings.CurrentMemberSettings.CurrentMemberExactOptions;
        Painter.PaintElementName(SelectedMember, PaintArgs, ExactOptions, true);

        if (Settings.CurrentMemberSettings.DisplayReferenceCount)
        {
            Point p = PaintArgs.TextView.GetPointFromLineAndColumn(SelectedMember.NameRange.Start.Line, SelectedMember.NameRange.End.Offset);
            p.Y -= 5;
            p.X -= 2;
            PaintArgs.TextView.DrawString(CurrentReferences.Count + "", p,
                new Font(FontFamily.GenericSansSerif, 7),
                OptPaintIt.DefaultMemberStyle().Color1.TrueColor
                );
        }

        if (Settings.CurrentMemberSettings.HighlightOtherReferences)
        {
            PaintOptions SiblingOptions = Settings.CurrentMemberSettings.CurrentMemberSiblingOptions;
            foreach (LanguageElement Element in CurrentReferences)
            {
                Painter.PaintElementName(Element, PaintArgs, SiblingOptions, false);
            }
        }
    }
    private Rectangle LargerRect(LanguageElement LE, TextView View)
    {
        Rectangle functionReturnValue = default(Rectangle);
        functionReturnValue = View.GetRectangleFromLanguageElement(LE);
        functionReturnValue.Inflate(new Size(4, 4));
        functionReturnValue.Width += 20;
        return functionReturnValue;
    }
    #endregion
}
